package com.nageoffer.jobtrain.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nageoffer.jobtrain.admin.common.biz.user.UserContext;
import com.nageoffer.jobtrain.admin.common.enums.ContestStatusEnum;
import com.nageoffer.jobtrain.admin.dao.entity.ContestDO;
import com.nageoffer.jobtrain.admin.dao.entity.ContestQuestionDO;
import com.nageoffer.jobtrain.admin.dao.entity.OjQuestionDO;
import com.nageoffer.jobtrain.admin.dao.mapper.ContestMapper;
import com.nageoffer.jobtrain.admin.dao.mapper.ContestQuestionMapper;
import com.nageoffer.jobtrain.admin.dao.mapper.OjQuestionMapper;
import com.nageoffer.jobtrain.admin.dto.req.*;
import com.nageoffer.jobtrain.admin.dto.resp.ContestAddOjQuestionRespDTO;
import com.nageoffer.jobtrain.admin.dto.resp.ContestEditRespDTO;
import com.nageoffer.jobtrain.admin.dto.resp.ContestPageRespDTO;
import com.nageoffer.jobtrain.admin.service.ContestService;
import com.nageoffer.jobtrain.admin.service.dto.ContestDetailDTO;
import com.nageoffer.jobtrain.admin.service.dto.OjQuestionContestDTO;
import com.nageoffer.jobtrain.common.web.exception.ClientException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 竞赛管理接口实现层
 * author：唐晨
 */
@Service
@RequiredArgsConstructor
public class ContestServiceImpl extends ServiceImpl<ContestMapper, ContestDO> implements ContestService {
    private final ContestMapper contestMapper;
    private final ContestQuestionMapper contestQuestionMapper;
    private final OjQuestionMapper ojQuestionMapper;

    @Override
    public IPage<ContestPageRespDTO> contestPageList(ContestPageReqDTO requestParam) {
        Page<ContestPageRespDTO> page = new Page<>(requestParam.getCurrent(), requestParam.getSize());
        List<ContestPageRespDTO> result = contestMapper.contestPageList(requestParam.getTitle(),
                requestParam.getBeginTime(), requestParam.getEndTime(), page);
        page.setRecords(result);
        return page;
    }

    @Override
    public ContestAddOjQuestionRespDTO addContestBaseInfo(ContestAddBaseInfoReqDTO requestParam) {
        // 验证竞赛基本信息是否合法
        checkContestDetail(ContestDetailDTO.builder()
                .title(requestParam.getTitle())
                .beginTime(requestParam.getBeginTime())
                .endTime(requestParam.getEndTime())
                .build());

        ContestDO contestDO = ContestDO.builder()
                .title(requestParam.getTitle())
                .createOperator(Long.valueOf(UserContext.getAdminUserId()))
                // 默认是未发布状态
                .status(ContestStatusEnum.UNPUBLISHED.getType())
                .beginTime(requestParam.getBeginTime())
                .updateOperator(Long.valueOf(UserContext.getAdminUserId()))
                .endTime(requestParam.getEndTime())
                .build();
        contestMapper.insert(contestDO);
        return ContestAddOjQuestionRespDTO.builder()
                .id(contestDO.getId())
                .build();
    }

    @Override
    public ContestEditRespDTO editContest(ContestEditReqDTO requestParam) {
        LambdaQueryWrapper<ContestDO> contestQueryWrapper = Wrappers.lambdaQuery(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getDelFlag, 0);
        ContestDO contestDO = contestMapper.selectOne(contestQueryWrapper);
        if (contestDO == null) {
            throw new ClientException("该竞赛基本信息不存在");
        }
        ContestEditRespDTO contestEdit = ContestEditRespDTO.builder()
                .title(contestDO.getTitle())
                .beginTime(contestDO.getBeginTime())
                .endTime(contestDO.getEndTime())
                .build();
        LambdaQueryWrapper<ContestQuestionDO> contestQuestionQueryWrapper = Wrappers.lambdaQuery(ContestQuestionDO.class)
                .eq(ContestQuestionDO::getContestId, requestParam.getId())
                .eq(ContestQuestionDO::getDelFlag, 0)
                .orderByAsc(ContestQuestionDO::getSortDesc);
        List<ContestQuestionDO> contestQuestionList = contestQuestionMapper.selectList(contestQuestionQueryWrapper);
        if (CollectionUtil.isNotEmpty(contestQuestionList)) {
            // 获取竞赛中所有的 OJ 题目 ID
            Map<Long, Integer> questionIdToDescMap = contestQuestionList.stream()
                    .collect(Collectors.toMap(ContestQuestionDO::getQuestionId, ContestQuestionDO::getSortDesc));

            // 根据 ID 查询 OJ 题目详情
            LambdaQueryWrapper<OjQuestionDO> ojQuestionQueryWrapper = Wrappers.lambdaQuery(OjQuestionDO.class)
                    .in(OjQuestionDO::getId, questionIdToDescMap.keySet())
                    .eq(OjQuestionDO::getDelFlag, 0);
            List<OjQuestionDO> ojQuestionList = ojQuestionMapper.selectList(ojQuestionQueryWrapper);

            // 将 OJ 问题列表转化为 OjQuestionContestDTO，并设置 desc 字段
            List<OjQuestionContestDTO> ojQuestionContestList = ojQuestionList.stream()
                    .map(ojQuestion -> {
                        OjQuestionContestDTO dto = BeanUtil.copyProperties(ojQuestion, OjQuestionContestDTO.class);
                        // 设置排序字段 desc
                        dto.setSortDesc(questionIdToDescMap.get(ojQuestion.getId()));
                        return dto;
                    })
                    .sorted(Comparator.comparingInt(OjQuestionContestDTO::getSortDesc))
                    .toList();
            contestEdit.setOjQuestionContestList(ojQuestionContestList);
        }
        return contestEdit;
    }

    @Override
    public void updateContest(ContestUpdateReqDTO requestParam) {
        checkContestDetail(ContestDetailDTO.builder()
                .id(requestParam.getId())
                .title(requestParam.getTitle())
                .beginTime(requestParam.getBeginTime())
                .endTime(requestParam.getEndTime())
                .build());
        // 判断该竞赛是否开赛
        LambdaQueryWrapper<ContestDO> queryWrapper = Wrappers.lambdaQuery(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getDelFlag, 0);
        if (contestMapper.selectOne(queryWrapper).getBeginTime().before(new Date())) {
            throw new ClientException("该竞赛已经开始了，禁止修改");
        }

        LambdaUpdateWrapper<ContestDO> updateWrapper = Wrappers.lambdaUpdate(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getDelFlag, 0);

        ContestDO contestDO = ContestDO.builder()
                .title(requestParam.getTitle())
                .beginTime(requestParam.getBeginTime())
                .updateOperator(Long.valueOf(UserContext.getAdminUserId()))
                .endTime(requestParam.getEndTime())
                .build();
        contestMapper.update(contestDO, updateWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteContest(ContestDeleteReqDTO requestParam) {
        LambdaQueryWrapper<ContestDO> queryWrapper = Wrappers.lambdaQuery(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getDelFlag, 0);
        ContestDO contestDO = contestMapper.selectOne(queryWrapper);
        if (contestDO == null) {
            throw new ClientException("该竞赛详情不存在");
        }
        // 判断该竞赛是否已经开赛，如果已经开赛，不能进行删除
        if (contestDO.getBeginTime().before(new Date())) {
            throw new ClientException("该竞赛已经开始比赛，禁止删除");
        }
        // 先删除竞赛-OJ表中的竞赛信息，再删除竞赛详情信息
        contestQuestionMapper.delete(new LambdaQueryWrapper<ContestQuestionDO>()
                .eq(ContestQuestionDO::getContestId, requestParam.getId()));

        LambdaUpdateWrapper<ContestDO> updateWrapper = Wrappers.lambdaUpdate(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .set(ContestDO::getDelFlag, 1);
        contestMapper.update(updateWrapper);
    }

    @Override
    public void publishContest(ContestPublishReqDTO requestParam) {
        LambdaQueryWrapper<ContestDO> contestQueryWrapper = Wrappers.lambdaQuery(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getDelFlag, 0);
        ContestDO contestDO = contestMapper.selectOne(contestQueryWrapper);
        if (contestDO == null) {
            throw new ClientException("该竞赛详情不存在，禁止发布");
        }
        if (Objects.equals(contestDO.getStatus(), ContestStatusEnum.PUBLISHING.getType())) {
            throw new ClientException("该竞赛已经发布");
        }
        // 判断该竞赛中是否含有OJ题目，如果不包含，禁止发布
        LambdaQueryWrapper<ContestQuestionDO> contestQuestionQueryWrapper = Wrappers.lambdaQuery(ContestQuestionDO.class)
                .eq(ContestQuestionDO::getContestId, requestParam.getId())
                .eq(ContestQuestionDO::getDelFlag, 0);
        List<ContestQuestionDO> contestQuestionList = contestQuestionMapper.selectList(contestQuestionQueryWrapper);
        if (CollectionUtil.isEmpty(contestQuestionList)) {
            throw new ClientException("该竞赛中暂无OJ题目，禁止发布");
        }
        LambdaUpdateWrapper<ContestDO> updateWrapper = Wrappers.lambdaUpdate(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .set(ContestDO::getStatus, ContestStatusEnum.PUBLISHING.getType())
                .set(ContestDO::getUpdateOperator, Long.valueOf(UserContext.getAdminUserId()));
        contestMapper.update(updateWrapper);
    }

    @Override
    public void revokePublishContest(ContestRevokePublishReqDTO requestParam) {
        LambdaQueryWrapper<ContestDO> contestQueryWrapper = Wrappers.lambdaQuery(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getDelFlag, 0);
        ContestDO contestDO = contestMapper.selectOne(contestQueryWrapper);
        if (contestDO == null) {
            throw new ClientException("该竞赛详情不存在");
        }
        if (contestDO.getBeginTime().before(new Date())) {
            throw new ClientException("该竞赛已经开赛，禁止撤销发布");
        }
        LambdaUpdateWrapper<ContestDO> updateWrapper = Wrappers.lambdaUpdate(ContestDO.class)
                .eq(ContestDO::getId, requestParam.getId())
                .set(ContestDO::getStatus, ContestStatusEnum.UNPUBLISHED.getType())
                .set(ContestDO::getUpdateOperator, Long.valueOf(UserContext.getAdminUserId()));
        contestMapper.update(updateWrapper);
    }

    public void checkContestDetail(ContestDetailDTO requestParam) {
        // 判断这个竞赛标题是否重复，添加的时候判断没有问题，修改的时候会有问题
        LambdaQueryWrapper<ContestDO> queryWrapper = Wrappers.lambdaQuery(ContestDO.class)
                // 当不为ID不为null的时候，才进行去除这个ID
                .ne(requestParam.getId() != null, ContestDO::getId, requestParam.getId())
                .eq(ContestDO::getTitle, requestParam.getTitle())
                .eq(ContestDO::getDelFlag, 0);
        ContestDO contestDO = baseMapper.selectOne(queryWrapper);
        if (contestDO != null) {
            throw new ClientException("该竞赛基本信息已存在");
        }
        // 判断该竞赛的开始结束时间设置是否有误
        if (requestParam.getBeginTime() == null || requestParam.getEndTime() == null) {
            throw new ClientException("竞赛时间格式设置有误");
        }

        if (requestParam.getBeginTime().before(new Date())) {
            throw new ClientException("该竞赛的开始时间不能早于当前时间");
        }
        if (requestParam.getEndTime().before(requestParam.getBeginTime())) {
            throw new ClientException("该竞赛的结束时间不能早于开始时间");
        }
    }
}
